'use strict';

const { Controller } = require('egg');
const AlipayFormData = require('alipay-sdk/lib/form').default;
const url = require('url');
const querystring = require('querystring');

const castObject = {
  WAIT_BUYER_PAY: 2,
  TRADE_CLOSED: 3,
  TRADE_SUCCESS: 4,
  TRADE_FINISHED: 5,
};

class AlipayController extends Controller {
  async pay() {
    const { ctx } = this;
    const { id } = ctx.params;
    const order = await ctx.model.Order.findByPk(id);
    if (!order) {
      ctx.throw(404, 'not_found');
      return;
    }

    if (order.status !== 0) {
      ctx.throw(403, 'order_dealt');
      return;
    }

    const { description, amount } = order;
    if (typeof ctx.session.confirm === 'undefined'
      || ctx.session.confirm === null) {
      ctx.session.confirm = {};
    }
    if (typeof ctx.session.confirm[id] === 'undefined'
      || ctx.session.confirm[id] === null) {
      const redirectUrl = '/index.html#/confirm?id=' + encodeURIComponent(id)
        + '&description=' + encodeURIComponent(description)
        + '&amount=' + encodeURIComponent(amount)
        + '&redirect=' + encodeURIComponent(ctx.originalUrl);
      ctx.redirect(redirectUrl);
      return;
    }
    if (!ctx.session.confirm[id]) {
      ctx.session.confirm[id] = undefined;
      ctx.throw(403, 'access_denied');
      return;
    }

    const { redirect } = ctx.query;
    if (redirect) {
      ctx.cookies.set('redirect', redirect);
    } else {
      ctx.cookies.set('redirect', '', { maxAge: 0 });
    }
    ctx.session.confirm[id] = undefined;
    await order.update({ status: 1 });
    this.app.initializeAlipay();
    await ctx.model.Alipay.create({ out_trade_no: id });
    const formData = new AlipayFormData();
    formData.addField('returnUrl', 'https://pay.zcxsythenew.xyz/pay/callback');
    formData.addField('notifyUrl', 'https://pay.zcxsythenew.xyz/pay/notify');
    formData.addField('bizContent', {
      out_trade_no: id,
      product_code: 'FAST_INSTANT_TRADE_PAY',
      total_amount: amount,
      subject: description,
    });
    ctx.body = await this.app.Alipay.pageExec('alipay.trade.page.pay',
      { formData });
  }

  callback() {
    const { ctx } = this;
    this.app.initializeAlipay();
    if (!this.app.Alipay.checkNotifySign(ctx.query)) throw new Error();

    const id = ctx.query.out_trade_no;
    let redirect = ctx.cookies.get('redirect');
    if (redirect) {
      const uri = url.parse(redirect);
      const query = querystring.parse(uri.query);
      query.out_trade_no = id;
      redirect = url.format({
        protocol: uri.protocol,
        hostname: uri.hostname,
        pathname: uri.pathname,
        port: uri.port,

        query,
        slashes: true,
      });
    }

    const redirectUrl = '/index.html#/order?id=' + encodeURIComponent(id)
      + ((typeof redirect === 'undefined' || redirect === null) ? '' : ('&redirect=' + encodeURIComponent(redirect)));
    ctx.cookies.set('redirect', '', { maxAge: 0 });
    ctx.redirect(redirectUrl);
  }

  async notify() {
    const { ctx } = this;
    this.app.initializeAlipay();
    if (!this.app.Alipay.checkNotifySign(ctx.request.body)) throw new Error();
    const {
      out_trade_no,
      trade_no,
      buyer_id,
      trade_status,
      total_amount,
    } = ctx.request.body;
    const item = await ctx.model.Alipay.findByPk(out_trade_no);
    if (typeof item === 'undefined' || item === null) {
      ctx.throw(404, 'not_found');
      return;
    }
    await item.update({ trade_no, buyer_id, trade_status, total_amount });
    const order = await ctx.model.Order.findByPk(out_trade_no);
    if (!order) {
      ctx.throw(500, 'server_error');
      return;
    }
    await order.update({ status: castObject[trade_status] });
    ctx.body = 'success';
  }

  async show() {
    const { ctx } = this;
    const { id } = ctx.params;
    const item = await ctx.model.Alipay.findByPk(id);
    if (typeof item === 'undefined' || item === null) {
      ctx.throw(404, 'not_found');
      return;
    }
    ctx.body = item;
  }

  async refresh() {
    const { ctx } = this;
    const { id } = ctx.params;
    const item = await ctx.model.Alipay.findByPk(id);
    if (typeof item === 'undefined' || item === null) {
      ctx.throw(404, 'not_found');
      return;
    }
    this.app.initializeAlipay();
    const result = await this.app.Alipay.exec('alipay.trade.query', {
      bizContent: {
        out_trade_no: id,
      },
    });
    const {
      tradeNo,
      buyerUserId,
      tradeStatus,
      totalAmount,
    } = result;
    await item.update({
      trade_no: tradeNo,
      buyer_id: buyerUserId,
      trade_status: tradeStatus,
      total_amount: totalAmount,
    });
    const order = await ctx.model.Order.findByPk(id);
    if (!order) {
      ctx.throw(500, 'server_error');
      return;
    }
    await order.update({ status: castObject[tradeStatus] });
    ctx.status = 204;
  }
}

module.exports = AlipayController;
